//////////////////////////////////////////////////////////////////////
//
//	Crytek Common Source code
//	
//	File:Cry_Matrix.h
//	Description: Common matrix class
//
//	History:
//	-Feb 27,2003: Created by Ivo Herzeg
//                
//
//////////////////////////////////////////////////////////////////////


#ifndef MATRIX34_H
#define MATRIX34_H 

#if _MSC_VER > 1000
# pragma once
#endif





///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// struct Matrix34_tpl
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
template <typename F> struct __passinreg Matrix34_tpl
{
	F m00,m01,m02,m03;
	F m10,m11,m12,m13;
	F m20,m21,m22,m23;

	//default constructor
#if defined(_DEBUG) && !defined(__SPU__)
	ILINE Matrix34_tpl() 
	{
		if (sizeof(F)==4)
		{
			uint32* p=(uint32*)&m00;
			p[ 0]=F32NAN;	p[ 1]=F32NAN;	p[ 2]=F32NAN;	p[ 3]=F32NAN;
			p[ 4]=F32NAN;	p[ 5]=F32NAN;	p[ 6]=F32NAN;	p[ 7]=F32NAN;
			p[ 8]=F32NAN;	p[ 9]=F32NAN;	p[10]=F32NAN;	p[11]=F32NAN;
		}
		if (sizeof(F)==8)
		{
			uint64* p=(uint64*)&m00;
			p[ 0]=F64NAN;	p[ 1]=F64NAN;	p[ 2]=F64NAN;	p[ 3]=F64NAN;
			p[ 4]=F64NAN;	p[ 5]=F64NAN;	p[ 6]=F64NAN;	p[ 7]=F64NAN;
			p[ 8]=F64NAN;	p[ 9]=F64NAN;	p[10]=F64NAN;	p[11]=F64NAN;
		}
	}
#else
	ILINE Matrix34_tpl(){};
#endif


	//ASSIGNMENT OPERATOR of identical Matrix33 types. 
	//The assignment operator has precedence over assignment constructor 
	//Matrix34 m; m=m34;
	ILINE Matrix34_tpl<F>& operator = (const Matrix34_tpl<F> &m) 
	{ 
		assert(m.IsValid());
		m00=m.m00; m01=m.m01; m02=m.m02; m03=m.m03; 
		m10=m.m10; m11=m.m11; m12=m.m12; m13=m.m13;
		m20=m.m20; m21=m.m21; m22=m.m22; m23=m.m23; 
		return *this;
	}




	//--------------------------------------------------------------------
	//----   implementation of the constructors                        ---
	//--------------------------------------------------------------------
	ILINE Matrix34_tpl<F>(	f32 v00, f32 v01, f32 v02, f32 v03,	f32 v10, f32 v11, f32 v12, f32 v13,	f32 v20, f32 v21, f32 v22, f32 v23)
	{ 		
		m00=v00; m01=v01; m02=v02; m03=v03; 
		m10=v10; m11=v11; m12=v12; m13=v13; 
		m20=v20; m21=v21; m22=v22; m23=v23; 
	}
	Matrix34_tpl( const Vec3_tpl<F>& s, const Quat_tpl<F>& q, const Vec3_tpl<F>& t=Vec3(ZERO) ) 
	{	
		Set(s, q, t); 
	}


	//CONSTRUCTOR for different types. It converts a Matrix33 into a Matrix34. 
	//Matrix34(m33);
	ILINE Matrix34_tpl<F>(const Matrix33_tpl<F>& m)
	{
		assert(m.IsValid());
		m00=m.m00;	m01=m.m01;	m02=m.m02;	m03=0;	
		m10=m.m10;	m11=m.m11;	m12=m.m12;	m13=0;
		m20=m.m20;	m21=m.m21;	m22=m.m22;	m23=0;
	}
	//CONSTRUCTOR for different types. It converts a Matrix33 into a Matrix34 and also converts between double/float. 
	//Matrix34(Matrix33);
	template<class F1> ILINE Matrix34_tpl<F>(const Matrix33_tpl<F1>& m)
  {
		assert(m.IsValid());
		m00=F(m.m00);		m01=F(m.m01);		m02=F(m.m02);		m03=0;	
		m10=F(m.m10);		m11=F(m.m11);		m12=F(m.m12);		m13=0;
		m20=F(m.m20);		m21=F(m.m21);		m22=F(m.m22);		m23=0;
	}


	//CONSTRUCTOR for different types. It converts a Matrix33 with a translation-vector into a Matrix34. 
	//Matrix34(m33,Vec3(1,2,3));
	ILINE Matrix34_tpl<F>(const Matrix33_tpl<F>& m, const Vec3_tpl<F>& t)
	{
		assert(m.IsValid());
		assert(t.IsValid());
		m00=m.m00;	m01=m.m01;	m02=m.m02;	m03=t.x;	
		m10=m.m10;	m11=m.m11;	m12=m.m12;	m13=t.y;
		m20=m.m20;	m21=m.m21;	m22=m.m22;	m23=t.z;
	}
	//CONSTRUCTOR for different types. It converts a Matrix33 with a translation-vector into a Matrix34 and also converts between double/float. 
	//Matrix34(Matrix33r,Vec3r(1,2,3));
	template<class F1> ILINE Matrix34_tpl<F>(const Matrix33_tpl<F1>& m, const Vec3_tpl<F1>& t)
	{
		assert(m.IsValid());
		assert(t.IsValid());
		m00=F(m.m00);	m01=F(m.m01);	m02=F(m.m02);	m03=F(t.x);	
		m10=F(m.m10);	m11=F(m.m11);	m12=F(m.m12);	m13=F(t.y);
		m20=F(m.m20);	m21=F(m.m21);	m22=F(m.m22);	m23=F(t.z);
	}



	//CONSTRUCTOR for identical types 
	//Matrix34 m=m34;  
	ILINE Matrix34_tpl<F>( const Matrix34_tpl<F>& m )
  {
		assert(m.IsValid());
		m00=m.m00;		m01=m.m01;		m02=m.m02;	m03=m.m03;
		m10=m.m10;		m11=m.m11;		m12=m.m12;	m13=m.m13;
		m20=m.m20;		m21=m.m21;		m22=m.m22;	m23=m.m23;
	}
	//CONSTRUCTOR for identical types which converts between double/float 
	//Matrix34 m=m34r;  
	template<class F1> ILINE Matrix34_tpl<F>( const Matrix34_tpl<F1>& m )
  {
		assert(m.IsValid());
		m00=F(m.m00);		m01=F(m.m01);		m02=F(m.m02);	m03=F(m.m03);	
		m10=F(m.m10);		m11=F(m.m11);		m12=F(m.m12);	m13=F(m.m13);
		m20=F(m.m20);		m21=F(m.m21);		m22=F(m.m22);	m23=F(m.m23);
	}

	//CONSTRUCTOR for different types. It converts a Matrix44 into a Matrix34. 
	//Needs to be 'explicit' because we loose the translation vector in the conversion process 
	//Matrix34(m44);
	ILINE explicit Matrix34_tpl<F>(const Matrix44_tpl<F>& m )
	{
		assert(m.IsValid());
		m00=m.m00;		m01=m.m01;		m02=m.m02;		m03=m.m03;	
		m10=m.m10;		m11=m.m11;		m12=m.m12;		m13=m.m13;
		m20=m.m20;		m21=m.m21;		m22=m.m22;		m23=m.m23;
	}
	//CONSTRUCTOR for different types. It converts a Matrix44 into a Matrix34 and converts between double/float. 
	//Needs to be 'explicit' because we loose the translation vector in the conversion process 
	//Matrix34(m44r);
	template<class F1> ILINE explicit Matrix34_tpl<F>(const Matrix44_tpl<F1>& m )
	{
		assert(m.IsValid());
		m00=F(m.m00);		m01=F(m.m01);		m02=F(m.m02);		m03=F(m.m03);	
		m10=F(m.m10);		m11=F(m.m11);		m12=F(m.m12);		m13=F(m.m13);
		m20=F(m.m20);		m21=F(m.m21);		m22=F(m.m22);		m23=F(m.m23);
	}



#if (defined(PS3) || defined(XENON))
	//CONSTRUCTOR for different types. It converts a Matrix34A into a Matrix34. 
	//Needs to be 'explicit' because we loose the 16byte alignment in the conversion process 
	//Matrix34(m34A);
	ILINE Matrix34_tpl<F>( const Matrix34A_tpl<F>& m ) //the F-type for Matrix34A is always 32bit-float
	{
		assert(m.IsValid());
		m00=m.m00;		m01=m.m01;		m02=m.m02;	m03=m.m03;	
		m10=m.m10;		m11=m.m11;		m12=m.m12;	m13=m.m13;
		m20=m.m20;		m21=m.m21;		m22=m.m22;	m23=m.m23;
	}
	//CONSTRUCTOR for different types. It converts a Matrix34A into a Matrix34 and converts the floats of the Matrix34A into doubles. 
	//Needs to be 'explicit' because we loose the 16byte alignment in the conversion process 
	//Matrix34r(m34A);
	template<class F1> ILINE Matrix34_tpl<F>( const Matrix34A_tpl<F1>& m ) //the F1-type for Matrix34A is always 32bit-float
	{
		assert(m.IsValid());
		m00=F(m.m00);		m01=F(m.m01);		m02=F(m.m02);	m03=F(m.m03);	
		m10=F(m.m10);		m11=F(m.m11);		m12=F(m.m12);	m13=F(m.m13);
		m20=F(m.m20);		m21=F(m.m21);		m22=F(m.m22);	m23=F(m.m23);
	}

	//CONSTRUCTOR for different types. It converts a Matrix44A into a Matrix34. 
	//Needs to be 'explicit' because we loose the translation vector and the 3rd row in the conversion process 
	//Matrix34(m44A);
	ILINE explicit Matrix34_tpl<F>(const Matrix44A_tpl<F>& m ) //the F-type for Matrix44A is always 32bit-float
	{
		assert(m.IsValid());
		m00=m.m00;		m01=m.m01;		m02=m.m02;		m03=m.m03;	
		m10=m.m10;		m11=m.m11;		m12=m.m12;		m13=m.m13;
		m20=m.m20;		m21=m.m21;		m22=m.m22;		m23=m.m23;
	}
	//CONSTRUCTOR for different types. It converts a Matrix44A into a Matrix34 and converts between double/float. 
	//Needs to be 'explicit' because we loose the translation vector and the 3rd row in the conversion process 
	//Matrix34(m44Ar);
	template<class F1> ILINE explicit Matrix34_tpl<F>(const Matrix44A_tpl<F1>& m ) //the F1-type for Matrix44A is always 32bit-float
	{
		assert(m.IsValid());
		m00=F(m.m00);		m01=F(m.m01);		m02=F(m.m02);		m03=F(m.m03);	
		m10=F(m.m10);		m11=F(m.m11);		m12=F(m.m12);		m13=F(m.m13);
		m20=F(m.m20);		m21=F(m.m21);		m22=F(m.m22);		m23=F(m.m23);
	}
#endif



	//Convert unit quaternion to matrix.
	explicit ILINE Matrix34_tpl<F>( const Quat_tpl<F>& q ) 
	{ 
		*this=Matrix33_tpl<F>(q); 
	}
	template<class F1> explicit ILINE Matrix34_tpl<F>( const Quat_tpl<F1>& q ) 
	{ 
		*this=Matrix33_tpl<F>(q); 
	}

	//Convert unit quaternion to matrix.
	explicit ILINE Matrix34_tpl<F>( const QuatT_tpl<F> &q ) 
	{
		*this=Matrix34_tpl<F>(Matrix33_tpl<F>(q.q),q.t); 
	}
	template<class F1> ILINE explicit Matrix34_tpl( const QuatT_tpl<F1> q ) 
	{
		*this=Matrix34_tpl<F>(Matrix33_tpl<F1>(q.q),q.t); 
	}



	explicit ILINE Matrix34_tpl<F>( const QuatTS_tpl<F> &q ) 
	{
		assert(q.q.IsValid());
		Vec3_tpl<F> v2=q.q.v+q.q.v;
		F xx=1-v2.x*q.q.v.x;	F yy=v2.y*q.q.v.y;	F xw=v2.x*q.q.w;
		F xy=v2.y*q.q.v.x;		F yz=v2.z*q.q.v.y;	F yw=v2.y*q.q.w;
		F xz=v2.z*q.q.v.x;		F zz=v2.z*q.q.v.z;	F zw=v2.z*q.q.w;
		m00=(1-yy-zz)*q.s;		m01=(xy-zw)*q.s;		m02=(xz+yw)*q.s;	m03=q.t.x;
		m10=(xy+zw)*q.s;			m11=(xx-zz)*q.s;		m12=(yz-xw)*q.s;	m13=q.t.y;
		m20=(xz-yw)*q.s;			m21=(yz+xw)*q.s;		m22=(xx-yy)*q.s;	m23=q.t.z;
	}
	template<class F1> ILINE explicit Matrix34_tpl<F>( const QuatTS_tpl<F1> &q ) 
	{
		assert(q.q.IsValid());
		Vec3_tpl<F1> v2=q.q.v+q.q.v;
		F1 xx=1-v2.x*q.q.v.x;		F1 yy=v2.y*q.q.v.y;		F1 xw=v2.x*q.q.w;
		F1 xy=v2.y*q.q.v.x;			F1 yz=v2.z*q.q.v.y;		F1 yw=v2.y*q.q.w;
		F1 xz=v2.z*q.q.v.x;			F1 zz=v2.z*q.q.v.z;		F1 zw=v2.z*q.q.w;
		m00=F((1-yy-zz)*q.s);		m01=F((xy-zw)*q.s);		m02=F((xz+yw)*q.s); m03=F(q.t.x);
		m10=F((xy+zw)*q.s);			m11=F((xx-zz)*q.s);		m12=F((yz-xw)*q.s);	m13=F(q.t.y);
		m20=F((xz-yw)*q.s);			m21=F((yz+xw)*q.s);		m22=F((xx-yy)*q.s);	m23=F(q.t.z);
	}


	ILINE explicit Matrix34_tpl<F>( const DualQuat_tpl<F> &q ) 
	{
		assert((fabs_tpl(1-(q.nq|q.nq)))<0.01); //check if unit-quaternion
		Vec3_tpl<F>  t=(q.nq.w*q.dq.v - q.dq.w*q.nq.v + q.nq.v%q.dq.v); //perfect for HLSL
		Vec3_tpl<F> v2=q.nq.v+q.nq.v;
		F xx=1-v2.x*q.nq.v.x;	F yy=v2.y*q.nq.v.y;	F xw=v2.x*q.nq.w;
		F xy=v2.y*q.nq.v.x;		F yz=v2.z*q.nq.v.y;	F yw=v2.y*q.nq.w;
		F xz=v2.z*q.nq.v.x;		F zz=v2.z*q.nq.v.z;	F zw=v2.z*q.nq.w;
		m00=1-yy-zz;	m01=xy-zw;	m02=xz+yw;	m03=t.x+t.x;
		m10=xy+zw;		m11=xx-zz;	m12=yz-xw;	m13=t.y+t.y;
		m20=xz-yw;		m21=yz+xw;	m22=xx-yy;	m23=t.z+t.z;
	}
	template<class F1> ILINE explicit Matrix34_tpl<F>( const DualQuat_tpl<F1> &q ) 
	{
		assert((fabs_tpl(1-(q.nq|q.nq)))<0.01); //check if unit-quaternion
		Vec3_tpl<F1>  t=(q.nq.w*q.dq.v - q.dq.w*q.nq.v + q.nq.v%q.dq.v); //perfect for HLSL
		Vec3_tpl<F1> v2=q.nq.v+q.nq.v;
		F1 xx=1-v2.x*q.nq.v.x;	F1 yy=v2.y*q.nq.v.y;	F1 xw=v2.x*q.nq.w;
		F1 xy=v2.y*q.nq.v.x;		F1 yz=v2.z*q.nq.v.y;	F1 yw=v2.y*q.nq.w;
		F1 xz=v2.z*q.nq.v.x;		F1 zz=v2.z*q.nq.v.z;	F1 zw=v2.z*q.nq.w;
		m00=F(1-yy-zz);	m01=F(xy-zw);	m02=F(xz+yw);	m03=F(t.x+t.x);
		m10=F(xy+zw);		m11=F(xx-zz);	m12=F(yz-xw);	m13=F(t.y+t.y);
		m20=F(xz-yw);		m21=F(yz+xw);	m22=F(xx-yy);	m23=F(t.z+t.z);
	}





	//apply scaling to the columns of the matrix.
	ILINE void ScaleColumn( const Vec3_tpl<F>& s)
  {
    m00*=s.x;		m01*=s.y;		m02*=s.z;
    m10*=s.x;		m11*=s.y;		m12*=s.z;
    m20*=s.x;		m21*=s.y;		m22*=s.z;
	}



	/*!
	*
	*  Initializes the Matrix34 with the identity.
	*
	*/
	void SetIdentity( void )
  {
		m00=1.0f;	m01=0.0f;	m02=0.0f;	m03=0.0f;
		m10=0.0f;	m11=1.0f;	m12=0.0f;	m13=0.0f;
		m20=0.0f;	m21=0.0f; m22=1.0f;	m23=0.0f;
	}

	ILINE static Matrix34_tpl<F> CreateIdentity( void )
	{
		Matrix34_tpl<F> m;
		m.SetIdentity();
		return m;
	}

	ILINE Matrix34_tpl(type_identity)
	{
		SetIdentity();
	}

	ILINE bool IsIdentity() const
	{
		return 0 == (fabs_tpl((F)1-m00) + fabs_tpl(m01) + fabs_tpl(m02) + fabs_tpl(m03) + fabs_tpl(m10) + fabs_tpl((F)1-m11) + fabs_tpl(m12) + fabs_tpl(m13) + fabs_tpl(m20) + fabs_tpl(m21) + fabs_tpl((F)1-m22)) + fabs_tpl(m23);
	}

	ILINE int IsZero() const
	{
		return 0 == (fabs_tpl(m00) + fabs_tpl(m01) + fabs_tpl(m02) + fabs_tpl(m03) + fabs_tpl(m10) + fabs_tpl(m11) + fabs_tpl(m12) + fabs_tpl(m13) + fabs_tpl(m20) + fabs_tpl(m21) + fabs_tpl(m22)) + fabs_tpl(m23);
	}

	/*!
	*  Create a rotation matrix around an arbitrary axis (Eulers Theorem).  
	*  The axis is specified as an normalized Vec3. The angle is assumed to be in radians.  
	*  This function also assumes a translation-vector and stores it in the right column.  
	*
	*  Example:
	*		Matrix34 m34;
	*		Vec3 axis=GetNormalized( Vec3(-1.0f,-0.3f,0.0f) );
	*		m34.SetRotationAA( 3.14314f, axis, Vec3(5,5,5) );
	*/
	ILINE void SetRotationAA(const F rad, const Vec3_tpl<F>& axis, const Vec3_tpl<F>& t=Vec3(ZERO) )	
	{
		*this=Matrix33_tpl<F>::CreateRotationAA(rad,axis); this->SetTranslation(t);
	}
	ILINE static Matrix34_tpl<F> CreateRotationAA( const F rad, const Vec3_tpl<F>& axis, const Vec3_tpl<F>& t=Vec3(ZERO) )	
	{	
		Matrix34_tpl<F> m34;  m34.SetRotationAA(rad,axis,t);	return m34;	
	}

	ILINE void SetRotationAA(const Vec3_tpl<F>& rot, const Vec3_tpl<F>& t=Vec3(ZERO) ) 
	{
		*this=Matrix33_tpl<F>::CreateRotationAA(rot); this->SetTranslation(t);
	}
	ILINE static Matrix34_tpl<F> CreateRotationAA( const Vec3_tpl<F>& rot, const Vec3_tpl<F>& t=Vec3(ZERO) )	
	{	
		Matrix34_tpl<F> m34;  m34.SetRotationAA(rot,t);	return m34;	
	}


	/*!
	* Create rotation-matrix about X axis using an angle.
	* The angle is assumed to be in radians. 
	* The translation-vector is set to zero.  
	*
	*  Example:
	*		Matrix34 m34;
	*		m34.SetRotationX(0.5f);
	*/
	ILINE void SetRotationX(const f32 rad, const Vec3_tpl<F>& t=Vec3(ZERO) )	
	{
		*this=Matrix33_tpl<F>::CreateRotationX(rad); this->SetTranslation(t);
	}
	ILINE static Matrix34_tpl<F> CreateRotationX( const f32 rad, const Vec3_tpl<F>& t=Vec3(ZERO) ) 
	{
		Matrix34_tpl<F> m34;  m34.SetRotationX(rad,t);	return m34;	
	}

	/*!
	* Create rotation-matrix about Y axis using an angle.
	* The angle is assumed to be in radians. 
	* The translation-vector is set to zero.  
	*
	*  Example:
	*		Matrix34 m34;
	*		m34.SetRotationY(0.5f);
	*/
	ILINE void SetRotationY(const f32 rad, const Vec3_tpl<F>& t=Vec3(ZERO) )	
	{
		*this=Matrix33_tpl<F>::CreateRotationY(rad);	this->SetTranslation(t);
	}
	ILINE static Matrix34_tpl<F> CreateRotationY( const f32 rad, const Vec3_tpl<F>& t=Vec3(ZERO) )	
	{	
		Matrix34_tpl<F> m34;  m34.SetRotationY(rad,t);	return m34;	
	}

	/*!
	* Create rotation-matrix about Z axis using an angle.
	* The angle is assumed to be in radians. 
	* The translation-vector is set to zero.  
	*
	*  Example:
	*		Matrix34 m34;
	*		m34.SetRotationZ(0.5f);
	*/
	ILINE void SetRotationZ(const f32 rad, const Vec3_tpl<F>& t=Vec3(ZERO) )	
	{
		*this=Matrix33_tpl<F>::CreateRotationZ(rad);  this->SetTranslation(t);
	}
	ILINE static Matrix34_tpl<F> CreateRotationZ( const f32 rad, const Vec3_tpl<F>& t=Vec3(ZERO) )	
	{	
		Matrix34_tpl<F> m34;  m34.SetRotationZ(rad,t);	return m34;	
	}


	/*!
	*
	* Convert three Euler angle to mat33 (rotation order:XYZ)
	* The Euler angles are assumed to be in radians. 
	* The translation-vector is set to zero.  
	*
	*  Example 1:
	*		Matrix34 m34;
	*		m34.SetRotationXYZ( Ang3(0.5f,0.2f,0.9f), translation );
	*
	*  Example 2:
	*		Matrix34 m34=Matrix34::CreateRotationXYZ( Ang3(0.5f,0.2f,0.9f), translation );
	*/
	ILINE void SetRotationXYZ( const Ang3_tpl<F>& rad, const Vec3_tpl<F>& t=Vec3(ZERO) ) 
	{
		assert(rad.IsValid());
		assert(t.IsValid());
		*this=Matrix33_tpl<F>::CreateRotationXYZ(rad); this->SetTranslation(t);
	}
	ILINE static Matrix34_tpl<F> CreateRotationXYZ( const Ang3_tpl<F>& rad, const Vec3_tpl<F>& t=Vec3(ZERO) ) 
	{	
		assert(rad.IsValid());
		assert(t.IsValid());
		Matrix34_tpl<F> m34;  m34.SetRotationXYZ(rad,t);	return m34;	
	}


	ILINE void SetRotationAA(F c, F s, Vec3_tpl<F> axis, const Vec3_tpl<F>& t=Vec3(ZERO) ) 
	{ 
		assert(axis.IsValid());
		assert(t.IsValid());
		*this=Matrix33_tpl<F>::CreateRotationAA(c, s, axis);
		m03=t.x; m13=t.y; m23=t.z;
	}
	ILINE static Matrix34_tpl<F> CreateRotationAA(F c, F s, Vec3_tpl<F> axis, const Vec3_tpl<F>& t=Vec3(ZERO) ) 
	{	
		Matrix34_tpl<F> m34;	m34.SetRotationAA(c,s,axis,t); return m34;	
	}

	ILINE void SetTranslationMat(  const Vec3_tpl<F>& v  ) 
	{
		m00=1.0f;	m01=0.0f;	m02=0.0f;	m03=v.x;
		m10=0.0f;	m11=1.0f;	m12=0.0f;	m13=v.y;
		m20=0.0f;	m21=0.0f;	m22=1.0f;	m23=v.z;
	}
	ILINE static Matrix34_tpl<F> CreateTranslationMat(  const Vec3_tpl<F>& v  ) 
	{	
		Matrix34_tpl<F> m34; m34.SetTranslationMat(v); return m34; 	
	}


	//NOTE: all vectors are stored in columns
	ILINE void SetFromVectors(const Vec3& vx, const Vec3& vy, const Vec3& vz, const Vec3& pos)	
	{
		m00=vx.x;		m01=vy.x;		m02=vz.x;		m03 = pos.x;
		m10=vx.y;		m11=vy.y;		m12=vz.y;		m13 = pos.y;
		m20=vx.z;		m21=vy.z;		m22=vz.z;		m23 = pos.z;
	}
	ILINE static Matrix34_tpl<F> CreateFromVectors(const Vec3_tpl<F>& vx, const Vec3_tpl<F>& vy, const Vec3_tpl<F>& vz, const Vec3_tpl<F>& pos) 
	{
		Matrix34_tpl<F> m; m.SetFromVectors(vx,vy,vz,pos); return m;
	}

	void InvertFast() 
	{ // in-place transposition
		assert(IsOrthonormal());
		F t; Vec3 v(m03,m13,m23);
		t=m01; m01=m10; m10=t; m03=-v.x*m00-v.y*m01-v.z*m20;
		t=m02; m02=m20; m20=t; m13=-v.x*m10-v.y*m11-v.z*m21;
		t=m12; m12=m21; m21=t; m23=-v.x*m20-v.y*m21-v.z*m22;
	}

	Matrix34_tpl<F> GetInvertedFast() const 
	{
		assert(IsOrthonormal());
		Matrix34_tpl<F> dst;
		dst.m00=m00;	dst.m01=m10;	dst.m02=m20;	dst.m03=-m03*m00-m13*m10-m23*m20;
		dst.m10=m01;	dst.m11=m11;	dst.m12=m21;	dst.m13=-m03*m01-m13*m11-m23*m21;
		dst.m20=m02;	dst.m21=m12;	dst.m22=m22;	dst.m23=-m03*m02-m13*m12-m23*m22;
		return dst;
	}

	//! transforms a vector. the translation is not beeing considered
	ILINE Vec3_tpl<F> TransformVector(const Vec3_tpl<F> &v) const  
	{
		assert(v.IsValid());
		return Vec3_tpl<F>(m00*v.x+m01*v.y+m02*v.z, m10*v.x+m11*v.y+m12*v.z, m20*v.x+m21*v.y+m22*v.z);
	}
	//! transforms a point and add translation vector
	ILINE Vec3_tpl<F> TransformPoint(const Vec3_tpl<F> &p) const  
	{
		assert(p.IsValid());
		return Vec3_tpl<F>( m00*p.x+m01*p.y+m02*p.z+m03, m10*p.x+m11*p.y+m12*p.z+m13, m20*p.x+m21*p.y+m22*p.z+m23);
	}

	//! Remove scale from matrix.
	void OrthonormalizeFast()
	{
		Vec3 x( m00,m10,m20 );
		Vec3 y( m01,m11,m21 );
		Vec3 z;
		x = x.GetNormalized();
		z = (x % y).GetNormalized();
		y = (z % x).GetNormalized();
		m00=x.x;		m10=x.y;		m20=x.z;
		m01=y.x;		m11=y.y;		m21=y.z;
		m02=z.x;		m12=z.y;		m22=z.z;
	}


	//determinant is ambiguous: only the upper-left-submatrix's determinant is calculated
	ILINE f32 Determinant() const		
	{
		return (m00*m11*m22) + (m01*m12*m20) + (m02*m10*m21) - (m02*m11*m20) - (m00*m12*m21) - (m01*m10*m22);
	}


	//--------------------------------------------------------------------------------
	//----                  helper functions to access matrix-members     ------------
	//--------------------------------------------------------------------------------

	F *GetData() { return &m00; }
	const F *GetData() const { return &m00; }

	ILINE F operator () (uint32 i, uint32 j) const {	assert ((i<3) && (j<4));	F* p_data=(F*)(&m00); 	return p_data[i*4+j];	}
	ILINE F& operator () (uint32 i, uint32 j)	{	assert ((i<3) && (j<4));	F* p_data=(F*)(&m00);		return p_data[i*4+j];	}

	ILINE void SetRow(int i, const Vec3_tpl<F> &v)	{	assert(i<3);	F* p=(F*)(&m00);	p[0+4*i]=v.x;	p[1+4*i]=v.y;	p[2+4*i]=v.z;		}

	ILINE const Vec3_tpl<F>& GetRow(int i) const	{	assert(i<3); return *(const Vec3_tpl<F>*)(&m00 + 4*i);	}
	ILINE const Vec4_tpl<F>& GetRow4(int i) const	{	assert(i<3); return *(const Vec4_tpl<F>*)(&m00 + 4*i);	}

	ILINE void SetColumn(int i, const Vec3_tpl<F> &v)	{	assert(i<4);	F* p=(F*)(&m00);	p[i+4*0]=v.x;	p[i+4*1]=v.y;	p[i+4*2]=v.z;		}
	ILINE Vec3_tpl<F> GetColumn(int i) const	{	assert(i<4);	F* p=(F*)(&m00);	return Vec3(p[i+4*0],p[i+4*1],p[i+4*2]);	}
	ILINE Vec3_tpl<F> GetColumn0() const	{ return Vec3_tpl<F>(m00,m10,m20);	}
	ILINE Vec3_tpl<F> GetColumn1() const	{ return Vec3_tpl<F>(m01,m11,m21);	}
	ILINE Vec3_tpl<F> GetColumn2() const	{ return Vec3_tpl<F>(m02,m12,m22);	}
	ILINE Vec3_tpl<F> GetColumn3() const	{ return Vec3_tpl<F>(m03,m13,m23);	}


	ILINE void SetTranslation( const Vec3_tpl<F>& t ) { m03=t.x;	m13=t.y; m23=t.z;	}
	ILINE Vec3_tpl<F> GetTranslation() const { return Vec3_tpl<F>(m03,m13,m23); }
	ILINE void ScaleTranslation (F s)			            {	m03*=s;		m13*=s;		m23*=s;		}
	ILINE Matrix34_tpl<F> AddTranslation( const Vec3_tpl<F>& t )	{	m03+=t.x;	m13+=t.y;	m23+=t.z;	return *this; }

	ILINE void SetRotation33( const Matrix33_tpl<F>& m33 ) 
	{ 
		m00=m33.m00;	m01=m33.m01; m02=m33.m02;	
		m10=m33.m10;	m11=m33.m11; m12=m33.m12;	
		m20=m33.m20;	m21=m33.m21; m22=m33.m22;	
	}

	//check if we have an orthonormal-base (general case, works even with reflection matrices)
	int IsOrthonormal(F threshold=0.001) const 
	{ 
		f32 d0=fabs_tpl(GetColumn0()|GetColumn1());	if 	(d0>threshold) return 0;	
		f32 d1=fabs_tpl(GetColumn0()|GetColumn2());	if 	(d1>threshold) return 0;	
		f32 d2=fabs_tpl(GetColumn1()|GetColumn2());	if 	(d2>threshold) return 0;	
		int a=(fabs_tpl(1-(GetColumn0()|GetColumn0())))<threshold;
		int b=(fabs_tpl(1-(GetColumn1()|GetColumn1())))<threshold;
		int c=(fabs_tpl(1-(GetColumn2()|GetColumn2())))<threshold;
		return a&b&c;
	}

	//check if we have an orthonormal-base (assuming we are using a right-handed coordinate system)
	int IsOrthonormalRH(F threshold=0.001) const 
	{ 
		int a=Vec3_tpl<F>::IsEquivalent(GetColumn0(),GetColumn1()%GetColumn2(),threshold);
		int b=Vec3_tpl<F>::IsEquivalent(GetColumn1(),GetColumn2()%GetColumn0(),threshold);
		int c=Vec3_tpl<F>::IsEquivalent(GetColumn2(),GetColumn0()%GetColumn1(),threshold);
		return a&b&c;
	}
	bool static IsEquivalent( const Matrix34_tpl<F>& m0, const Matrix34_tpl<F>& m1, F e=VEC_EPSILON) 
	{
		return  ( 
			(fabs_tpl(m0.m00-m1.m00)<=e) && (fabs_tpl(m0.m01-m1.m01)<=e) && (fabs_tpl(m0.m02-m1.m02)<=e) && (fabs_tpl(m0.m03-m1.m03)<=e) && 
			(fabs_tpl(m0.m10-m1.m10)<=e) && (fabs_tpl(m0.m11-m1.m11)<=e) && (fabs_tpl(m0.m12-m1.m12)<=e) && (fabs_tpl(m0.m13-m1.m13)<=e) && 
			(fabs_tpl(m0.m20-m1.m20)<=e) && (fabs_tpl(m0.m21-m1.m21)<=e) && (fabs_tpl(m0.m22-m1.m22)<=e) && (fabs_tpl(m0.m23-m1.m23)<=e) 
			);	
	}

	bool IsValid() const
	{
		if (!NumberValid(m00)) return false;	if (!NumberValid(m01)) return false;	if (!NumberValid(m02)) return false;	if (!NumberValid(m03)) return false;
		if (!NumberValid(m10)) return false;	if (!NumberValid(m11)) return false;	if (!NumberValid(m12)) return false;	if (!NumberValid(m13)) return false;
		if (!NumberValid(m20)) return false;	if (!NumberValid(m21)) return false;	if (!NumberValid(m22)) return false;	if (!NumberValid(m23)) return false;
		return true;
	}


 
	/*!
	* Create a matrix with SCALING, ROTATION and TRANSLATION (in this order).
	*
	*  Example 1:
	*		Matrix m34;
	*		m34.SetMatrix( Vec3(1,2,3), quat, Vec3(11,22,33)  );
	*/
	ILINE void Set( const Vec3_tpl<F>& s, const Quat_tpl<F>& q, const Vec3_tpl<F>& t=Vec3(ZERO)  ) 
	{
		assert(s.IsValid());
		assert(q.IsUnit(0.1f));
		assert(t.IsValid());
		F vxvx=q.v.x*q.v.x;		F vzvz=q.v.z*q.v.z;		F vyvy=q.v.y*q.v.y; 
		F vxvy=q.v.x*q.v.y;		F vxvz=q.v.x*q.v.z;		F vyvz=q.v.y*q.v.z; 
		F svx=q.w*q.v.x;			F svy=q.w*q.v.y;			F svz=q.w*q.v.z;
		m00=(1-(vyvy+vzvz)*2)*s.x;	m01=(vxvy-svz)*2*s.y;				m02=(vxvz+svy)*2*s.z;				m03=t.x;
		m10=(vxvy+svz)*2*s.x;				m11=(1-(vxvx+vzvz)*2)*s.y;	m12=(vyvz-svx)*2*s.z;				m13=t.y;
		m20=(vxvz-svy)*2*s.x;				m21=(vyvz+svx)*2*s.y;				m22=(1-(vxvx+vyvy)*2)*s.z;	m23=t.z;
	}
	ILINE static Matrix34_tpl<F> Create(  const Vec3_tpl<F>& s, const Quat_tpl<F>& q, const Vec3_tpl<F>& t=Vec3(ZERO) ) 
	{
		Matrix34_tpl<F> m34;	m34.Set(s,q,t); return m34;	
	}


	/*!
	* Create scaling-matrix.
	* The translation-vector is set to zero.		
	*
	*  Example 1:
	*		Matrix m34;
	*		m34.SetScale( Vec3(0.5f, 1.0f, 2.0f) );	
	*  Example 2:
	*		Matrix34 m34 = Matrix34::CreateScale( Vec3(0.5f, 1.0f, 2.0f) );
	*/
	ILINE void SetScale( const Vec3_tpl<F> &s, const Vec3_tpl<F>& t=Vec3(ZERO)  ) 
	{
		*this=Matrix33::CreateScale(s); this->SetTranslation(t);
	}
	ILINE static Matrix34_tpl<F> CreateScale(  const Vec3_tpl<F> &s, const Vec3_tpl<F>& t=Vec3(ZERO)   )	
	{ 	
		Matrix34_tpl<F> m34;  m34.SetScale(s,t);	return m34;	
	}


	ILINE Matrix44_tpl<F> GetTransposed() const
	{
		Matrix44_tpl<F> tmp;
		tmp.m00=m00; tmp.m01=m10; tmp.m02=m20; tmp.m03=0;
		tmp.m10=m01; tmp.m11=m11; tmp.m12=m21; tmp.m13=0;
		tmp.m20=m02; tmp.m21=m12; tmp.m22=m22; tmp.m23=0;
		tmp.m30=m03; tmp.m31=m13; tmp.m32=m23; tmp.m33=1;
		return tmp;
	}

	/*!
	* calculate a real inversion of a Matrix34
	* an inverse-matrix is an UnDo-matrix for all kind of transformations 
	* 
	*  Example 1:
	*		Matrix34 im34; im34.Invert();
	*
	*  Example 2:
	*   Matrix34 im34 = m34.GetInverted();
	*/
	ILINE void Invert( void ) 
	{
		//rescue members	
		Matrix34_tpl<F>	m=*this;
		// calculate 12 cofactors
		m00= m.m22*m.m11-m.m12*m.m21;
		m10= m.m12*m.m20-m.m22*m.m10;
		m20= m.m10*m.m21-m.m20*m.m11;
		m01= m.m02*m.m21-m.m22*m.m01;
		m11= m.m22*m.m00-m.m02*m.m20;
		m21= m.m20*m.m01-m.m00*m.m21;
		m02= m.m12*m.m01-m.m02*m.m11;
		m12= m.m02*m.m10-m.m12*m.m00;
		m22= m.m00*m.m11-m.m10*m.m01;
		m03= (m.m22*m.m13*m.m01 + m.m02*m.m23*m.m11 + m.m12*m.m03*m.m21) - (m.m12*m.m23*m.m01 + m.m22*m.m03*m.m11 + m.m02*m.m13*m.m21);
		m13= (m.m12*m.m23*m.m00 + m.m22*m.m03*m.m10 + m.m02*m.m13*m.m20) - (m.m22*m.m13*m.m00 + m.m02*m.m23*m.m10 + m.m12*m.m03*m.m20);
		m23= (m.m20*m.m11*m.m03 + m.m00*m.m21*m.m13 + m.m10*m.m01*m.m23) - (m.m10*m.m21*m.m03 + m.m20*m.m01*m.m13 + m.m00*m.m11*m.m23);
		// calculate determinant
		F det=1.0f/(m.m00*m00+m.m10*m01+m.m20*m02);
		assert( fabs_tpl(det)>(F)0.00000001 );
		// calculate matrix inverse/
		m00*=det; m01*=det; m02*=det; m03*=det;
		m10*=det; m11*=det; m12*=det; m13*=det;
		m20*=det; m21*=det; m22*=det; m23*=det;
	}
	ILINE Matrix34_tpl<F> GetInverted() const 
	{	
		Matrix34_tpl<F> dst=*this; dst.Invert(); return dst; 
	}

	/*!
	*  Name:             ReflectMat34
	*  Description:      reflect a rotation matrix with respect to a plane.	
	*
	*  Example:
	*		Vec3 normal( 0.0f,-1.0f, 0.0f);
	*		Vec3 pos(0,1000,0);
	*		Matrix34 m34=CreateReflectionMat( pos, normal );
	*/
	ILINE static Matrix34_tpl<F> CreateReflectionMat ( const Vec3_tpl<F>& p, const Vec3_tpl<F>& n )
	{
		Matrix34_tpl<F> m;	
		F vxy   = -2.0f * n.x * n.y;
		F vxz   = -2.0f * n.x * n.z;
		F vyz   = -2.0f * n.y * n.z;
		F pdotn = 2.0f * (p|n);
		m.m00=1.0f-2.0f*n.x*n.x;	m.m01=vxy;    						m.m02=vxz;    						m.m03=pdotn*n.x;
		m.m10=vxy;  							m.m11=1.0f-2.0f*n.y*n.y; 	m.m12=vyz;    						m.m13=pdotn*n.y;
		m.m20=vxz;  							m.m21=vyz;   							m.m22=1.0f-2.0f*n.z*n.z; 	m.m23=pdotn*n.z;
		return m;
	}

	AUTO_STRUCT_INFO
};

///////////////////////////////////////////////////////////////////////////////
// Typedefs                                                                  //
///////////////////////////////////////////////////////////////////////////////

typedef Matrix34_tpl<f32> Matrix34;
typedef Matrix34_tpl<real> Matrix34r;

#if (defined(WIN32) || defined(WIN64) || defined(LINUX) || defined(APPLE) || defined(ORBIS))
typedef Matrix34_tpl<f32> Matrix34A;
#elif defined(DURANGO)
typedef DEFINE_ALIGNED_DATA(Matrix34_tpl<f32>, Matrix34A, 16); 
#endif

//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
//-------------       implementation of Matrix34      ------------------------------
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------

//! multiply all matrix's values by f and return the matrix
template<class F> 
ILINE Matrix34_tpl<F> operator * (const Matrix34_tpl<F>& m, const f32 f)	
{ 
	assert(m.IsValid());
  Matrix34_tpl<F> r;
	r.m00=m.m00*f;	r.m01=m.m01*f;	r.m02=m.m02*f;	r.m03=m.m03*f; 
	r.m10=m.m10*f;	r.m11=m.m11*f;	r.m12=m.m12*f;	r.m13=m.m13*f;
	r.m20=m.m20*f;	r.m21=m.m21*f;	r.m22=m.m22*f;	r.m23=m.m23*f;
	return r;
}	

/*!
*  multiplication of Matrix34 by a (column) Vec3.
*  This function transforms the given input Vec3
*  into the coordinate system defined by this matrix.
*
*  Example:
*   Matrix34 m34;
*   Vec3 vector(44,55,66);
*   Vec3 result = m34*vector;
*/
template<class F> 
ILINE Vec3_tpl<F> operator * (const Matrix34_tpl<F>& m, const Vec3_tpl<F> &p) {
	assert(m.IsValid());
	assert(p.IsValid());
	Vec3_tpl<F> tp;
	tp.x	=	m.m00*p.x + m.m01*p.y + m.m02*p.z + m.m03;
	tp.y	=	m.m10*p.x + m.m11*p.y + m.m12*p.z + m.m13;
	tp.z	=	m.m20*p.x + m.m21*p.y + m.m22*p.z + m.m23;
	return	tp;
}


template<class F1, class F2> 
ILINE Matrix34_tpl<F1> operator*(const Matrix34_tpl<F1> &l, const Diag33_tpl<F2> &r) {
	assert(l.IsValid());
	assert(r.IsValid());
	Matrix34_tpl<F1> m;
	m.m00=l.m00*r.x;	m.m01=l.m01*r.y;	m.m02=l.m02*r.z;	m.m03=l.m03;
	m.m10=l.m10*r.x;	m.m11=l.m11*r.y;	m.m12=l.m12*r.z;	m.m13=l.m13;
	m.m20=l.m20*r.x;	m.m21=l.m21*r.y;	m.m22=l.m22*r.z;	m.m23=l.m23;
	return m;
}
template<class F1, class F2> 
ILINE Matrix34_tpl<F1>& operator *= (Matrix34_tpl<F1>& l, const Diag33_tpl<F2>& r) {
	assert(l.IsValid());
	assert(r.IsValid());
	l.m00*=r.x;	l.m01*=r.y;	l.m02*=r.z; 
	l.m10*=r.x;	l.m11*=r.y;	l.m12*=r.z;
	l.m20*=r.x;	l.m21*=r.y;	l.m22*=r.z;
	return l;
}
template<class F1, class F2> 
ILINE Matrix34_tpl<F1>& operator += (Matrix34_tpl<F1>& l, const Matrix34_tpl<F2>& r) {
	assert(l.IsValid());
	assert(r.IsValid());
	l.m00+=r.m00;	l.m01+=r.m01;	l.m02+=r.m02; l.m03+=r.m03; 
	l.m10+=r.m10;	l.m11+=r.m11;	l.m12+=r.m12; l.m13+=r.m13;
	l.m20+=r.m20;	l.m21+=r.m21;	l.m22+=r.m22; l.m23+=r.m23;
	return l;
}

/*!
*  Implements the multiplication operator: Matrix34=Matrix34*Matrix33
*
*  Matrix33 and Matrix44 are specified in collumn order for a right-handed coordinate-system.        
*  AxB = operation B followed by operation A.  
*  A multiplication takes 27 muls and 24 adds. 
*
*  Example:
*   Matrix34 m34=Matrix33::CreateRotationX(1.94192f);;
*   Matrix33 m33=Matrix34::CreateRotationZ(3.14192f);
*	  Matrix34 result=m34*m33;
*/
template<class F> 
ILINE Matrix34_tpl<F> operator * (const Matrix34_tpl<F>& l, const Matrix33_tpl<F>& r) {
	assert(l.IsValid());
	assert(r.IsValid());
	Matrix34_tpl<F> m;
	m.m00 = l.m00*r.m00 + l.m01*r.m10 + l.m02*r.m20;
	m.m10 = l.m10*r.m00 + l.m11*r.m10 + l.m12*r.m20;
	m.m20 = l.m20*r.m00 + l.m21*r.m10 + l.m22*r.m20;
	m.m01 = l.m00*r.m01 + l.m01*r.m11 + l.m02*r.m21;
	m.m11 = l.m10*r.m01 + l.m11*r.m11 + l.m12*r.m21;
	m.m21 = l.m20*r.m01 + l.m21*r.m11 + l.m22*r.m21;
	m.m02 = l.m00*r.m02 + l.m01*r.m12 + l.m02*r.m22;
	m.m12 = l.m10*r.m02 + l.m11*r.m12 + l.m12*r.m22;
	m.m22 = l.m20*r.m02 + l.m21*r.m12 + l.m22*r.m22;
	m.m03 = l.m03;
	m.m13 = l.m13;
	m.m23 = l.m23;
	return m;
}



/*!
*  Implements the multiplication operator: Matrix34=Matrix34*Matrix34
*
*  Matrix34 is specified in collumn order.        
*  AxB = rotation B followed by rotation A.  
*  This operation takes 36 mults and 27 adds. 
*
*  Example:
*   Matrix34 m34=Matrix34::CreateRotationX(1.94192f, Vec3(11,22,33));;
*   Matrix34 m34=Matrix33::CreateRotationZ(3.14192f);
*	  Matrix34 result=m34*m34;
*/
template<class F> 
ILINE Matrix34_tpl<F> operator * (const Matrix34_tpl<F>& l, const Matrix34_tpl<F>& r) {
	assert(l.IsValid());
	assert(r.IsValid());
	Matrix34_tpl<F> m;
	m.m00 = l.m00*r.m00 + l.m01*r.m10 + l.m02*r.m20;
	m.m10 = l.m10*r.m00 + l.m11*r.m10 + l.m12*r.m20;
	m.m20 = l.m20*r.m00 + l.m21*r.m10 + l.m22*r.m20;
	m.m01 = l.m00*r.m01 + l.m01*r.m11 + l.m02*r.m21;
	m.m11 = l.m10*r.m01 + l.m11*r.m11 + l.m12*r.m21;
	m.m21 = l.m20*r.m01 + l.m21*r.m11 + l.m22*r.m21;
	m.m02 = l.m00*r.m02 + l.m01*r.m12 + l.m02*r.m22;
	m.m12 = l.m10*r.m02 + l.m11*r.m12 + l.m12*r.m22;
	m.m22 = l.m20*r.m02 + l.m21*r.m12 + l.m22*r.m22;
	m.m03 = l.m00*r.m03 + l.m01*r.m13 + l.m02*r.m23 + l.m03;
	m.m13 = l.m10*r.m03 + l.m11*r.m13 + l.m12*r.m23 + l.m13;
	m.m23 = l.m20*r.m03 + l.m21*r.m13 + l.m22*r.m23 + l.m23;
	return m;
}

template<class F> 
ILINE Matrix34_tpl<F> operator * (const Matrix34A_tpl<F>& l, const Matrix34_tpl<F>& r) {
	assert(l.IsValid());
	assert(r.IsValid());
	Matrix34_tpl<F> m;
	m.m00 = l.m00*r.m00 + l.m01*r.m10 + l.m02*r.m20;
	m.m10 = l.m10*r.m00 + l.m11*r.m10 + l.m12*r.m20;
	m.m20 = l.m20*r.m00 + l.m21*r.m10 + l.m22*r.m20;
	m.m01 = l.m00*r.m01 + l.m01*r.m11 + l.m02*r.m21;
	m.m11 = l.m10*r.m01 + l.m11*r.m11 + l.m12*r.m21;
	m.m21 = l.m20*r.m01 + l.m21*r.m11 + l.m22*r.m21;
	m.m02 = l.m00*r.m02 + l.m01*r.m12 + l.m02*r.m22;
	m.m12 = l.m10*r.m02 + l.m11*r.m12 + l.m12*r.m22;
	m.m22 = l.m20*r.m02 + l.m21*r.m12 + l.m22*r.m22;
	m.m03 = l.m00*r.m03 + l.m01*r.m13 + l.m02*r.m23 + l.m03;
	m.m13 = l.m10*r.m03 + l.m11*r.m13 + l.m12*r.m23 + l.m13;
	m.m23 = l.m20*r.m03 + l.m21*r.m13 + l.m22*r.m23 + l.m23;
	return m;
}

/*!
*  Implements the multiplication operator: Matrix44=Matrix34*Matrix44
*
*  Matrix44 and Matrix34 are specified in collumn order.         
*	 AxB = rotation B followed by rotation A.  
*  This operation takes 48 mults and 36 adds.  
*
*  Example:
*   Matrix34 m34=Matrix33::CreateRotationX(1.94192f);;
*   Matrix44 m44=Matrix33::CreateRotationZ(3.14192f);
*	  Matrix44 result=m34*m44;
*/
template<class F> 
ILINE Matrix44_tpl<F> operator * (const Matrix34_tpl<F>& l, const Matrix44_tpl<F>& r) {
	assert(l.IsValid());
	assert(r.IsValid());
	Matrix44_tpl<F> m;
	m.m00 = l.m00*r.m00 + l.m01*r.m10 + l.m02*r.m20 + l.m03*r.m30;
	m.m10 = l.m10*r.m00 + l.m11*r.m10 + l.m12*r.m20 + l.m13*r.m30;
	m.m20 = l.m20*r.m00 + l.m21*r.m10 + l.m22*r.m20 + l.m23*r.m30;
	m.m30 = r.m30;
	m.m01 = l.m00*r.m01 + l.m01*r.m11 + l.m02*r.m21 + l.m03*r.m31;
	m.m11 = l.m10*r.m01 + l.m11*r.m11 + l.m12*r.m21 + l.m13*r.m31;
	m.m21 = l.m20*r.m01 + l.m21*r.m11 + l.m22*r.m21 + l.m23*r.m31;
	m.m31 = r.m31;
	m.m02 = l.m00*r.m02 + l.m01*r.m12 + l.m02*r.m22 + l.m03*r.m32;
	m.m12 = l.m10*r.m02 + l.m11*r.m12 + l.m12*r.m22 + l.m13*r.m32;
	m.m22 = l.m20*r.m02 + l.m21*r.m12 + l.m22*r.m22 + l.m23*r.m32;
	m.m32 = r.m32;
	m.m03 = l.m00*r.m03 + l.m01*r.m13 + l.m02*r.m23 + l.m03*r.m33;
	m.m13 = l.m10*r.m03 + l.m11*r.m13 + l.m12*r.m23 + l.m13*r.m33;
	m.m23 = l.m20*r.m03 + l.m21*r.m13 + l.m22*r.m23 + l.m23*r.m33;
	m.m33 = r.m33;
	return m;
}
template<class F> 
ILINE Matrix44_tpl<F> operator * (const Matrix34A_tpl<F>& l, const Matrix44_tpl<F>& r) {
	assert(l.IsValid());
	assert(r.IsValid());
	Matrix44_tpl<F> m;
	m.m00 = l.m00*r.m00 + l.m01*r.m10 + l.m02*r.m20 + l.m03*r.m30;
	m.m10 = l.m10*r.m00 + l.m11*r.m10 + l.m12*r.m20 + l.m13*r.m30;
	m.m20 = l.m20*r.m00 + l.m21*r.m10 + l.m22*r.m20 + l.m23*r.m30;
	m.m30 = r.m30;
	m.m01 = l.m00*r.m01 + l.m01*r.m11 + l.m02*r.m21 + l.m03*r.m31;
	m.m11 = l.m10*r.m01 + l.m11*r.m11 + l.m12*r.m21 + l.m13*r.m31;
	m.m21 = l.m20*r.m01 + l.m21*r.m11 + l.m22*r.m21 + l.m23*r.m31;
	m.m31 = r.m31;
	m.m02 = l.m00*r.m02 + l.m01*r.m12 + l.m02*r.m22 + l.m03*r.m32;
	m.m12 = l.m10*r.m02 + l.m11*r.m12 + l.m12*r.m22 + l.m13*r.m32;
	m.m22 = l.m20*r.m02 + l.m21*r.m12 + l.m22*r.m22 + l.m23*r.m32;
	m.m32 = r.m32;
	m.m03 = l.m00*r.m03 + l.m01*r.m13 + l.m02*r.m23 + l.m03*r.m33;
	m.m13 = l.m10*r.m03 + l.m11*r.m13 + l.m12*r.m23 + l.m13*r.m33;
	m.m23 = l.m20*r.m03 + l.m21*r.m13 + l.m22*r.m23 + l.m23*r.m33;
	m.m33 = r.m33;
	return m;
}




#endif //MATRIX_H

